package com.xoa.service.WFE.WFEFactory;

import com.xoa.dao.WFE.WFEFlowTypeModelMapper;
import com.xoa.dao.workflow.FlowProcessMapper;
import com.xoa.dao.workflow.FlowRunMapper;
import com.xoa.dao.workflow.FlowRunPrcsMapper;
import com.xoa.model.enclosure.Attachment;
import com.xoa.model.flowxml.FlowOutXml;
import com.xoa.model.flowxml.FlowProcessXml;
import com.xoa.model.flowxml.FlowTypeXml;
import com.xoa.model.users.Users;
import com.xoa.model.workflow.FlowDetailInfo;
import com.xoa.model.workflow.FlowProcess;
import com.xoa.model.workflow.FlowTypeModel;
import com.xoa.service.WFE.WFE;
import com.xoa.service.workflow.JobClassifyService;
import com.xoa.util.CookiesUtil;
import com.xoa.util.DateFormat;
import com.xoa.util.GetAttachmentListUtil;
import com.xoa.util.ToJson;
import com.xoa.util.common.L;
import com.xoa.util.common.PHPSerializerUtils;
import com.xoa.util.common.StringUtils;
import com.xoa.util.common.session.SessionUtils;
import com.xoa.util.common.wrapper.BaseWrapper;
import org.jdom2.CDATA;
import org.jdom2.Document;
import org.jdom2.Element;
import org.jdom2.input.SAXBuilder;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.web.multipart.MultipartFile;
import org.xml.sax.InputSource;

import javax.servlet.http.Cookie;
import javax.servlet.http.HttpServletRequest;
import java.io.InputStream;
import java.lang.reflect.Field;
import java.lang.reflect.Method;
import java.util.*;
@SuppressWarnings("all")
@Service
public class WFEFlowType extends WFE{

    @Autowired
    protected WFEFlowTypeModelMapper wfeFlowTypeModelMapper;

    @Autowired
    JobClassifyService classifyService;

    @Autowired
    FlowProcessMapper processMapper;

    @Autowired
    protected FlowRunMapper flowRunMapper;

    @Autowired
    protected FlowRunPrcsMapper flowRunPrcsMapper;


    /**
     *
     * @param flowTypeModel
     * @return
     */
    @Transactional
    public ToJson<FlowTypeModel> saveFlow(FlowTypeModel flowTypeModel) {
        ToJson<FlowTypeModel> toJson = new ToJson<FlowTypeModel>();
        try {
            wfeFlowTypeModelMapper.save(flowTypeModel);
            toJson.setMsg("ok");
            toJson.setFlag(0);
            toJson.setObject(flowTypeModel);
        } catch (Exception e) {
            L.e("保存异常："+e);
            toJson.setMsg("error");
            toJson.setFlag(1);
        }
        return toJson;
    }


    public ToJson<FlowTypeModel> selectAllFlow(Integer flowId) {
        ToJson<FlowTypeModel> toJson = new ToJson<FlowTypeModel>();
        try {
            FlowTypeModel flowTypeModel =  wfeFlowTypeModelMapper.queryOneObject(flowId);
            if (!StringUtils.checkNull(flowTypeModel.getViewDept())) {
                flowTypeModel.setViewDeptName(departmentService.getDeptNameByDeptId(flowTypeModel.getViewDept(), ","));
            }
            if (!StringUtils.checkNull(flowTypeModel.getViewRole())) {
                flowTypeModel.setViewRoleName(usersPrivService.getPrivNameByPrivId(flowTypeModel.getViewRole(), ","));
            }
            if (!StringUtils.checkNull(flowTypeModel.getViewUser())) {
                flowTypeModel.setViewUserName(usersService.getUserNameById(flowTypeModel.getViewUser()));
            }
//            if(!StringUtils.checkNull(flowTypeModel.getAttachmentId())){
//                Attachment attachment = enclosureService.findByAttachId(Integer.valueOf(flowTypeModel.getAttachmentId()));
//                if(attachment!=null){
//                    String sqlType= "xoa" + (String) request.getSession().getAttribute(
//                            "loginDateSouse");
//                    attachment.setAttUrl("AID="+attachment.getAid()+"&COMPANY="+sqlType+"&YM="+attachment.getYm()+"&ATTACHMENT_ID="+attachment.getAttachId()+"&ATTACHMENT_NAME="+attachment.getAttachName()+"&MODULE="+GetAttachmentListUtil.MODULE_WORKFLOW);
//                }
//                List sss= new ArrayList<Attachment>();
//                sss.add(attachment);
//                flowTypeModel.setAttachment(sss);
//            }
//            flowTypeModel.setAttachment(GetAttachmentListUtil.returnAttachment(flowTypeModel.getAttachmentName(),flowTypeModel.getAttachmentId(),"xoa1001",GetAttachmentListUtil.MODULE_WORKFLOW));
            toJson.setFlag(0);
            toJson.setMsg("ok");
            toJson.setObject(flowTypeModel);
        } catch (Exception e) {
            toJson.setFlag(1);
            toJson.setMsg("error");
        }
        return toJson;
    }

    /**
     * 创建作者:   张勇
     * 创建日期:   2017/5/23 17:31
     * 方法介绍:   自定义属性所有查询，根据flowId
     * 参数说明:
     *
     * @return
     */
    public ToJson<FlowTypeModel> selectAllFlow(Integer flowId,HttpServletRequest request) {
        ToJson<FlowTypeModel> toJson = new ToJson<FlowTypeModel>();
        try {
            FlowTypeModel flowTypeModel =  wfeFlowTypeModelMapper.queryOneObject(flowId);
            if (!StringUtils.checkNull(flowTypeModel.getViewDept())) {
                flowTypeModel.setViewDeptName(departmentService.getDeptNameByDeptId(flowTypeModel.getViewDept(), ","));
            }
            if (!StringUtils.checkNull(flowTypeModel.getViewRole())) {
                flowTypeModel.setViewRoleName(usersPrivService.getPrivNameByPrivId(flowTypeModel.getViewRole(), ","));
            }
            if (!StringUtils.checkNull(flowTypeModel.getViewUser())) {
                flowTypeModel.setViewUserName(usersService.getUserNameById(flowTypeModel.getViewUser()));
            }
            String sqlType= "xoa" + (String) request.getSession().getAttribute(
                    "loginDateSouse");
            String attachmentStrs= flowTypeModel.getAttachmentId();
            String attachmentStrName=flowTypeModel.getAttachmentName();
            List<Attachment> list=null;
            if(!StringUtils.checkNull(attachmentStrs)&&!StringUtils.checkNull(attachmentStrName)){
                list= GetAttachmentListUtil.returnAttachment(attachmentStrName,attachmentStrs,sqlType,GetAttachmentListUtil.MODULE_WORKFLOW);
            }
            flowTypeModel.setAttachment(list);
//            flowTypeModel.setAttachment(GetAttachmentListUtil.returnAttachment(flowTypeModel.getAttachmentName(),flowTypeModel.getAttachmentId(),"xoa1001",GetAttachmentListUtil.MODULE_WORKFLOW));
            toJson.setFlag(0);
            toJson.setMsg("ok");
            toJson.setObject(flowTypeModel);
        } catch (Exception e) {
            toJson.setFlag(1);
            toJson.setMsg("error");
        }
        return toJson;
    }

    public ToJson<FlowTypeModel> quertBySortId(Integer flowId) {
        ToJson<FlowTypeModel> toJson = new ToJson<FlowTypeModel>();

        List<FlowTypeModel> datas = wfeFlowTypeModelMapper.selectBySortid(flowId);
        if (datas != null && datas.size() > 0) {
            toJson.setObj(datas);
            toJson.setFlag(0);
            toJson.setMsg("success");
        } else {
            toJson.setFlag(1);
            toJson.setMsg("no data");
        }

        return toJson;
    }


    public ToJson<FlowTypeModel> flowBySearch(String searchValue, Integer flowId) {

        ToJson<FlowTypeModel> toJson = new ToJson<FlowTypeModel>();
        if (StringUtils.checkNull(searchValue)) {
            toJson.setFlag(1);
            toJson.setMsg("搜索字段不能为空");
            return toJson;
        }

        List<FlowTypeModel> datas = wfeFlowTypeModelMapper.selectBySearch(searchValue, flowId);
        if (datas != null && datas.size() > 0) {
            toJson.setObj(datas);
            toJson.setFlag(0);
            toJson.setMsg("success");
        } else {
            toJson.setFlag(1);
            toJson.setMsg("no data");
        }
        return toJson;
    }

    /**
     * 创建作者:   张勇
     * 创建日期:   2017/5/23 19:35
     * 方法介绍:   自定义属性根据flowId修改
     * 参数说明:
     * @return
     */
    @Transactional
    public ToJson<FlowTypeModel> updateFlow(FlowTypeModel flowTypeModel){
        ToJson<FlowTypeModel> toJson = new ToJson<FlowTypeModel>();
        try{
            int a = wfeFlowTypeModelMapper.updateFlowTypeModel(flowTypeModel);
            if(a>0){
                toJson.setFlag(0);
                toJson.setMsg("ok");
            }else {
                toJson.setFlag(1);
                toJson.setMsg("err");
            }

        }catch (Exception e){
            toJson.setFlag(1);
            toJson.setMsg("error");
            L.e("错误消息："+e);
        }
        return toJson;
    }


    public ToJson<FlowTypeModel> selectFlowAuthOrSearch(HttpServletRequest request, String searchValue, Integer sortId){
        Map<String,Object> param =new HashMap<String,Object>();
        ToJson<FlowTypeModel> toJson =new ToJson<FlowTypeModel>();
        Cookie redisSessionCookie = CookiesUtil.getCookieByName(request, "redisSessionId");
        Users user= SessionUtils.getSessionInfo(request.getSession(),Users.class,new Users(),redisSessionCookie);
        if(user.getUid()==null){
            toJson.setFlag(1);
            toJson.setMsg("无法获取用户信息");
            return toJson;
        }

        List<Integer> sirtIds = classifyService.getSortChildId(sortId,new ArrayList<Integer>());
        StringBuffer sirtIdsTr =new StringBuffer();
        int i=0;
        for(Integer integer:sirtIds){
            sirtIdsTr.append(integer);
            if(i!=sirtIds.size()){
                sirtIdsTr.append(",");
            }
            i++;
        }
        Integer[] arry=new Integer[]{};
        arry=sirtIds.toArray(arry);
        String privOther= user.getUserPrivOther()==null?"":user.getUserPrivOther();//辅助角色
        String privIds[] =privOther.split(",");
        String deptOther = user.getDeptIdOther()==null?"":user.getDeptIdOther();//辅助部门
        String deptIds[] =deptOther.split(",");
        param.put("user", user.getUserId()==null?"":user.getUserId());
        param.put("deptId",user.getDeptId()==null?"":user.getDeptId());
        param.put("privId", user.getUserPriv()==null?"":user.getUserPriv());
        if(privIds!=null&&privIds.length>0){
            param.put("privIds",privIds);
        }
        if(deptIds!=null&&deptIds.length>0){
            param.put("deptIds",deptIds);
        }
        param.put("searchValue",searchValue);
        L.w(arry);
        if(sortId!=null){
            param.put("sortId",sirtIdsTr.toString());
        }

        List<FlowTypeModel> datas = wfeFlowTypeModelMapper.selectFlowAuthOrSearch(param);

        if(datas!=null&&datas.size()>0){
            toJson.setFlag(0);
            toJson.setMsg("成功");
            toJson.setObj(datas);
        }else{
            toJson.setFlag(1);
            toJson.setMsg("失败");
        }
        return toJson;
    }


    public ToJson<FlowDetailInfo> selectFlowDetailInfo(Integer flowId) {
        ToJson<FlowDetailInfo> ret = new ToJson<FlowDetailInfo>();
        if(flowId==null){
            ret.setFlag(1);
            ret.setMsg("流程Id不能为空");
            return ret;
        }
        FlowDetailInfo info = wfeFlowTypeModelMapper.selectFlowDetailInfo(flowId);
        //断点处理
        List<FlowProcess> datas =processMapper.selectPricByFlowId(flowId);

        //处理是否可回退
        if(info==null){
            ret.setFlag(1);
            ret.setMsg("没有信息");
        }else{
            int cutNum=   cutNumber(datas);
            info.setCutNumber(cutNum);
            ret.setObject(info);
            ret.setFlag(0);
            ret.setMsg("成功");
        }
        return ret;
    }

    private Integer cutNumber(List<FlowProcess> datas){
        if(datas==null||datas.size()==0){
            return 0;
        }
        int res=0;
        for(int i=0;i<datas.size()-1;i++){
            FlowProcess now=datas.get(i);
            if(StringUtils.checkNull(now.getPrcsTo())){
                res++;
            }else if("0,".equals(now.getPrcsTo())){
                res++;
            }
//            FlowProcess now=datas.get(i);
//            FlowProcess next=datas.get(i+1);
//            if(now.getPrcsId()!=null){
//                if(next.getPrcsId()!=null){
//                    if(now.getPrcsId()+1==next.getPrcsId()){
//                    }else{
//                        res++;
//                    }
//                }else{
//                    res++;
//                }
//            }else{
//                res++;
//            }
        }
        return res;
    }


    /**
     * 创建作者:   张勇
     * 创建日期:   2017/6/22 10:55
     * 方法介绍:   根据flowId和formId查询一条数据
     * 参数说明:
     * @return
     */

    public FlowTypeModel selectOnlyType(Integer flowId,Integer formId){
        return wfeFlowTypeModelMapper.selectOnlyType(flowId,formId);
    }

    /**
     * 创建作者:   张勇
     * 创建日期:   2017/7/7 10:47
     * 方法介绍:   查询说明和附件
     * 参数说明:
     * @return
     */
    public ToJson<FlowTypeModel> selectAttachmentAndInstructions(Integer flowId, Integer formId,String sqlType) {
        ToJson<FlowTypeModel> toJson = new ToJson<FlowTypeModel>(1,"error");
        try {
            FlowTypeModel flowTypeModel = wfeFlowTypeModelMapper.selectAttachmentAndInstructions(flowId,formId);
            if (flowTypeModel.getAttachmentName() != null && flowTypeModel.getAttachmentId() != null) {
                flowTypeModel.setAttachment(GetAttachmentListUtil.returnAttachment(flowTypeModel.getAttachmentName(), flowTypeModel.getAttachmentId(), sqlType, GetAttachmentListUtil.MODULE_WORKFLOW));
            } else {
                flowTypeModel.setAttachmentName("");
                flowTypeModel.setAttachmentId("");
            }
            toJson.setObject(flowTypeModel);
            toJson.setFlag(0);
            toJson.setMsg("ok");
        }catch (Exception e){
            L.e("FlowTypeServiceImpl selectAttachmentAndInstructions:"+e);
        }
        return toJson;
    }

    public FlowTypeModel sfTbyrunId(Integer runId){
        FlowTypeModel flowTypeModel = wfeFlowTypeModelMapper.sflowType(runId);
        return flowTypeModel;
    }


    public BaseWrapper getFlowOutModel(Integer flowId) throws Exception{
        BaseWrapper wrapper =new BaseWrapper();
        FlowOutXml flowOutXml =new FlowOutXml();
        FlowTypeModel flowTypeModel = wfeFlowTypeModelMapper.queryOneObject(flowId);
        FlowTypeXml typeXml = invokeFlowData(flowTypeModel,FlowTypeXml.class);
        List<FlowProcess> datas =processMapper.selectPricByFlowId(flowId);
        List<FlowProcessXml> pro=new ArrayList<FlowProcessXml>();
        for(FlowProcess flowProcess:datas){
            FlowProcessXml processXml = invokeFlowData(flowProcess,FlowProcessXml.class);
            pro.add(processXml);
        }
        flowOutXml.setProcess(pro);
        flowOutXml.setBaseInfo(typeXml);
        wrapper.setData(writeFileUsingJDOM(flowOutXml));
        wrapper.setMsg(flowTypeModel.getFlowName());
        return wrapper;
    }

    public BaseWrapper intputFlowFile(Integer flowId,MultipartFile file) {
        BaseWrapper wrapper =new BaseWrapper();
        try(InputStream inputStream = file.getInputStream()){
            //clean process with flowId same
            processMapper.deleteByFlowId(flowId);
            readFlowXml(flowId,inputStream);
            wrapper.setFlag(true);
        }catch (Exception e){
            e.printStackTrace();
        }
        return wrapper;
    }

    @Transactional(rollbackFor = RuntimeException.class)
    public BaseWrapper deleteFlow(Integer flowId)  {
        BaseWrapper wrapper =new BaseWrapper();
        wrapper.setStatus(true);
        wrapper.setFlag(false);
        //Clean process with flowId same
        try{
            processMapper.deleteByFlowId(flowId);
            wfeFlowTypeModelMapper.deleteByFlowId(flowId);
            List<Integer> runIds = flowRunMapper.getRunIdsByFlowId(flowId);
            flowRunMapper.deleteByFlowId(flowId);
            if(runIds!=null&&runIds.size()>0)
                flowRunPrcsMapper.deleteByRunIds(runIds);
            wrapper.setFlag(true);

        }catch (Exception e){
            e.printStackTrace();
            throw  new RuntimeException();
        }

        return wrapper;
    }


    public BaseWrapper clearFlow(Integer flowId) {
        BaseWrapper wrapper =new BaseWrapper();
        wrapper.setStatus(true);
        wrapper.setFlag(false);
        //Clean process with flowId same
        try{
            List<Integer> runIds = flowRunMapper.getRunIdsByFlowId(flowId);
            flowRunMapper.deleteByFlowId(flowId);
            if(runIds!=null&&runIds.size()>0)
                flowRunPrcsMapper.deleteByRunIds(runIds);
            wrapper.setFlag(true);
        }catch (Exception e){
            e.printStackTrace();
            throw  new RuntimeException();
        }
        return wrapper;
    }

    private  void readFlowXml(Integer flowId,InputStream read) throws  Exception{
        SAXBuilder sb=new SAXBuilder();
        InputSource source = new InputSource(read);
        Document doc=sb.build(source);//构造文档对象
        Element root=doc.getRootElement();//获取根元素，即文档中的<HD>
        List baseInfos=root.getChildren("BaseInfo");//取名字为disk的所有元素
        for(Object element:baseInfos){
            FlowTypeModel flowTypeModel =  resolveDatas(element,FlowTypeXml.class,FlowTypeModel.class);
            flowTypeModel.setFlowId(flowId);
            flowTypeModel.setFlowName(null);
            flowTypeModel.setFormId(null);
            flowTypeModel.setFlowSort(null);
            wfeFlowTypeModelMapper.updateFlowTypeModel(flowTypeModel);
        }
        List process = root.getChildren("Process");
        for(Object element:process){
            FlowProcess flowProcess = resolveDatas(element,FlowProcessXml.class,FlowProcess.class);
            flowProcess.setId(null);
            flowProcess.setFlowId(flowId);
            flowProcess.setFileuploadPriv(PHPSerializerUtils.unSerializer(flowProcess.getFileuploadPriv()));
            flowProcess.setImguploadPriv(PHPSerializerUtils.unSerializer(flowProcess.getImguploadPriv()));
            processMapper.insert(flowProcess);
        }
        read.close();
    }

    private <T> T resolveDatas(Object data,Class clazz1, Class<T> clazz) throws  Exception{
        String  checkCulm=  "MANAGE_USER,QUERY_USER,NEW_USER,DEPT_ID,PRCS_USER,PRCS_DEPT,PRCS_PRIV,AUTO_USER_OP,AUTO_USER,MAIL_TO";
        T ret =clazz.newInstance();
        Element element=(Element)data;//依此遍历所有的disk节点
        Field[] fields =clazz1.getDeclaredFields();
        for(Field field:fields){
            String name = field.getName();
            if(checkCulm.indexOf(name)>0) continue;
            String value = element.getChildText(field.getName());
            if(StringUtils.checkNull(value)) continue;
            String caml =StringUtils.underline2Camel(name,true);
            if(caml.length()==2){
                caml.toLowerCase();
            }
            Field child = clazz.getDeclaredField(caml);
            child.setAccessible(true);
            field.setAccessible(true);
            Method set_Method = clazz.getMethod("set"
                            +SessionUtils.getMethodName(caml),
                    child.getType());
            Class clazzz= child.getType();
            set_Method.setAccessible(true);
            if(clazzz ==  String.class){
                set_Method.invoke(ret, value);
            }
            if(clazzz == Integer.class){
                set_Method.invoke(ret,Integer.parseInt(value));
            }
            if(clazzz == Byte.class){
                set_Method.invoke(ret,Byte.parseByte(value));
            }
            if(clazzz == Double.class){
                set_Method.invoke(ret,Double.parseDouble(value));
            }
            if(clazzz ==  Float.class){
                set_Method.invoke(ret,Float.parseFloat(value));
            }
            if(clazzz == Date.class){
                if(!value.equals(   "2000-01-01 00:00:00")){
                    set_Method.invoke(ret, DateFormat.getDate(value));
                }

            }
        }
        return  ret;
    }

    //处理映射关系
    private <T> T invokeFlowData(Object object, Class<T> clazz) {
        try{
            T ret = clazz.newInstance();
            Field[] fields=clazz.getDeclaredFields();
            for(Field field:fields){
                String name = field.getName();
                String  camlStr=null;
                if(name.length()==2){
                    camlStr=name.toLowerCase();
                }else{
                    camlStr= StringUtils.underline2Camel(name,true);
                }

                Field child=object.getClass().getDeclaredField(camlStr);
                if(child!=null){
                    child.setAccessible(true);
                    field.setAccessible(true);
                    Method set_Method = clazz.getMethod("set"
                                    + name,
                            field.getType());
                    set_Method.setAccessible(true);
                    set_Method.invoke(ret, child.get(object));
                }
            }
            return ret;
        }catch (Exception e){
            e.printStackTrace();
        }
        return null;
    }
    private  Document    writeFileUsingJDOM(FlowOutXml outXml) throws Exception {
        Document doc = new Document();
        doc.setRootElement(new Element("WorkFlow"));
        if (outXml != null) {
            Element element = new Element("BaseInfo");
            addConet(element, outXml.getBaseInfo());
            doc.getRootElement().addContent(element);
            addConetList(doc, outXml.getProcess());
        }
        return doc;

    }

    private  void addConetList(Document doc, List<FlowProcessXml> process) throws Exception {
        for (FlowProcessXml processXml : process) {
            Element element = new Element("Process");
            Field[] fields = processXml.getClass().getDeclaredFields();
            for (Field field : fields) {
                field.setAccessible(true);
                CDATA cdata=  new CDATA(field.getName()).setText(field.get(processXml)==null?" ":field.get(processXml).toString());
                element.addContent(new Element(field.getName()).addContent(cdata));
            }
            doc.getRootElement().addContent(element);
        }
    }

    private  void addConet(Element element, FlowTypeXml baseInfo) throws Exception {
        Field[] fields = baseInfo.getClass().getDeclaredFields();
        for (Field field : fields) {
            field.setAccessible(true);
            CDATA cdata= new CDATA(field.getName()).setText(field.get(baseInfo)==null?" ":field.get(baseInfo).toString());
            element.addContent(new Element(field.getName()).addContent(cdata));
        }
    }
}
